﻿using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using D.UtilCore.Dtos;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;

namespace D.UtilCore.JwtUtil
{
    /// <summary>
    /// 一个包含各种事件委托的类（通过实例并通过Events属性赋值来为不同的事件操作提供自定义的逻辑）
    /// </summary>
    public class AppJwtBearerEvents : JwtBearerEvents
    {
        /// <summary>
        /// 当收到请求时（此时还未获取到Token）
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        /// <remarks>
        /// 可在此自定义Token获取方式，并将获取的Token赋值到 context.Token（记得将Scheme从字符串中移除）
        /// 只要我们赋值的Token既非Null也非Empty，那后续验证就会使用该Token
        /// </remarks>
        public override Task MessageReceived(MessageReceivedContext context)
        {
            Console.WriteLine("-------------- MessageReceived Begin --------------");

            base.MessageReceived(context);
            if (context.Result != null)
            {
                return Task.CompletedTask;
            }

            Console.WriteLine($"Scheme: {context.Scheme.Name}");

            #region 以下是自定义Token获取方式示例（实际上也是默认方式）

            string authorization = context.Request.Headers[HeaderNames.Authorization];
            if (string.IsNullOrEmpty(authorization))
            {
                context.NoResult();
                return Task.CompletedTask;
            }

            if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
            {
                context.Token = authorization["Bearer ".Length..].Trim();
            }

            if (string.IsNullOrEmpty(context.Token))
            {
                context.NoResult();
                return Task.CompletedTask;
            }

            #endregion

            Console.WriteLine("-------------- MessageReceived End --------------");

            return Task.CompletedTask;
        }

        /// <summary>
        /// Token验证通过后
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task TokenValidated(TokenValidatedContext context)
        {
            Console.WriteLine("-------------- TokenValidated Begin --------------");

            base.TokenValidated(context);
            if (context.Result != null)
            {
                return Task.CompletedTask;
            }

            Console.WriteLine($"User Name: {context.Principal.Identity.Name}");
            Console.WriteLine($"Scheme: {context.Scheme.Name}");

            var token = context.SecurityToken;
            Console.WriteLine($"Token Id: {token.Id}");
            Console.WriteLine($"Token Issuer: {token.Issuer}");
            Console.WriteLine($"Token Valid From: {token.ValidFrom}");
            Console.WriteLine($"Token Valid To: {token.ValidTo}");

            Console.WriteLine($"Token SecurityKey: {token.SecurityKey}");

            if (token.SigningKey is SymmetricSecurityKey ssk)
            {
                Console.WriteLine($"Token SigningKey: {ssk.Key}");
            }
            else
            {
                Console.WriteLine($"Token SigningKey: {token.SigningKey}");
            }

            Console.WriteLine("-------------- TokenValidated End --------------");

            return Task.CompletedTask;
        }

        /// <summary>
        /// 由于认证过程中抛出异常，导致的身份认证失败后
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task AuthenticationFailed(AuthenticationFailedContext context)
        {
            Console.WriteLine("-------------- AuthenticationFailed Begin --------------");

            base.AuthenticationFailed(context);
            if (context.Result != null)
            {
                return Task.CompletedTask;
            }

            Console.WriteLine($"Scheme: {context.Scheme.Name}");
            Console.WriteLine($"Exception: {context.Exception}");

            Console.WriteLine("-------------- AuthenticationFailed End --------------");
            return Task.CompletedTask;
        }

        /// <summary>
        /// 质询
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task Challenge(JwtBearerChallengeContext context)
        {
            Console.WriteLine("-------------- Challenge Begin --------------");

            //base.Challenge(context);
            //if (context.Handled)
            //{
            //    return Task.CompletedTask;
            //}

            // 终止默认的返回结果
            context.HandleResponse();
            string token = context.Request.Headers["Authorization"].ToString()?.Replace("Bearer ", "");

            if (string.IsNullOrEmpty(token))
            {
                // 验证失败返回
                var result = JsonConvert.SerializeObject(new ApiResult<int>() { Code = StatusCodeEnum.Unauthorized, ErrorMsg = "Token不能为空" });
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = StatusCodes.Status200OK;
                context.Response.WriteAsync(result);
                return Task.FromResult(result);
            }

            try
            {
                JwtSecurityTokenHandler tokenheader = new();
                ClaimsPrincipal claimsPrincipal = tokenheader.ValidateToken(token, new TokenValidationParameters
                {
                    RequireExpirationTime = true,
                    ValidateIssuer = true,//验证创建该令牌的发布者
                    ValidateLifetime = true,//检查令牌是否未过期，以及发行者的签名密钥是否有效
                    ValidateAudience = false,//确保令牌的接收者有权接收它
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(BaseApi.appConfig.JwtConfig.SecurityKey)),
                    ValidIssuer = BaseApi.appConfig.JwtConfig.Issuer, //验证创建该令牌的发布者

                }, out SecurityToken securityToken);
            }
            catch (SecurityTokenExpiredException)
            {
                Console.WriteLine($"Scheme: {context.Scheme.Name}");
                Console.WriteLine($"Authenticate Failure: {context.AuthenticateFailure}");
                Console.WriteLine($"Error: {context.Error}");
                Console.WriteLine($"Error Description: {context.ErrorDescription}");
                Console.WriteLine($"Error Uri: {context.ErrorUri}");
                Console.WriteLine("-------------- Challenge End --------------");

                // 验证失败返回
                var result = JsonConvert.SerializeObject(new ApiResult<int>() { Code = StatusCodeEnum.Unauthorized, ErrorMsg = "登录已过期" });
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = StatusCodes.Status200OK;
                context.Response.WriteAsync(result);
                return Task.FromResult(result);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Scheme: {context.Scheme.Name}");
                Console.WriteLine($"Authenticate Failure: {context.AuthenticateFailure}");
                Console.WriteLine($"Error: {context.Error}");
                Console.WriteLine($"Error Description: {context.ErrorDescription}");
                Console.WriteLine($"Error Uri: {context.ErrorUri}");
                Console.WriteLine("-------------- Challenge End --------------");

                // 验证失败返回
                var result = JsonConvert.SerializeObject(new ApiResult<int>() { Code = StatusCodeEnum.Unauthorized, ErrorMsg = "Token令牌无效" });
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = StatusCodes.Status200OK;
                context.Response.WriteAsync(result);
                return Task.FromResult(result);
            }

            return Task.CompletedTask;
        }

        /// <summary>
        /// 禁止403
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task Forbidden(ForbiddenContext context)
        {
            Console.WriteLine("-------------- Forbidden Begin --------------");

            base.Forbidden(context);
            if (context.Result != null)
            {
                return Task.CompletedTask;
            }

            Console.WriteLine($"Scheme: {context.Scheme.Name}");

            Console.WriteLine("-------------- Forbidden End --------------");
            return Task.CompletedTask;
        }
    }
}
